package iscript.coreFramework.data
{
		
	import flash.events.*;
	import flash.net.*;
	import flash.system.System;
	
	public class DataCSV extends URLLoader
	{
		
		
		private var FieldSeperator		: String
		private var FieldEnclosureToken : String
		private var RecordsetDelimiter	: String
		
		private var Header 				: Array
		private var EmbededHeader 		: Boolean
		private var HeaderOverwrite 	: Boolean
		
		private var SortField			: *
		private var SortSequence		: String
		
		
		/**
		 *   TODO Constructor description ...
		 * 
		 *   @param request URLRequest
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function DataCSV( request : URLRequest = null )
		{
			flash.system.System.useCodePage=true;
			fieldSeperator 		= ','
			fieldEnclosureToken = '"'
			recordsetDelimiter 	= '\r'
			
			header = new Array()
			embededHeader = true
			headerOverwrite = false
			
			if( request )
				load( request )
			this.dataFormat = URLLoaderDataFormat.TEXT
			addEventListener( Event.COMPLETE, decode )
			
		}
		
		
		
		// -> getter
		
		
		
		/**
		 *   TODO Getter description ...
		 * 
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function get fieldSeperator() : String
		{
			return FieldSeperator
		}
		
		
		
		/**
		 *   TODO Getter description ...
		 * 
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function get fieldEnclosureToken() : String
		{
			return FieldEnclosureToken
		}
		
		
		
		/**
		 *   TODO Getter description ...
		 * 
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function get recordsetDelimiter() : String
		{
			return RecordsetDelimiter
		}
		
		
		
		/**
		 *   TODO Getter description ...
		 * 
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function get embededHeader() : Boolean
		{
			return EmbededHeader
		}
		
		
		
		/**
		 *   TODO Getter description ...
		 * 
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function get headerOverwrite()  : Boolean 
		{
			return HeaderOverwrite
		}
		
		
		
		/**
		 *   TODO Getter description ...
		 * 
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function get header() : Array 
		{
			return Header
		}
		
		
		
		/**
		 *   TODO Getter description ...
		 * 
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function get headerHasValues () : Boolean
		{
			var check : Boolean
			try {
				if ( Header.length > 0 ) check = true
			} catch ( e : Error ) {
				check = false
			} finally {
				return check
			}
		}
		
		
		
		/**
		 *   TODO Getter description ...
		 * 
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function get dataHasValues () : Boolean
		{
			var check : Boolean
			try {
				if ( data.length > 0 ) check = true
			} catch ( e : Error ) {
				check = false
			} finally {
				return check
			}
		}
		
		
		
		// -> setter
		
		
		
		/**
		 *   TODO Setter description ...
		 * 
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function set fieldSeperator( value : String ) : void
		{
			FieldSeperator = value
		}
		
		
		
		/**
		 *   TODO Getter description ...
		 * 
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function set fieldEnclosureToken( value : String ) : void
		{
			FieldEnclosureToken = value
		}
		
		
		
		/**
		 *   TODO Setter description ...
		 * 
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function set recordsetDelimiter( value : String ) : void
		{
			RecordsetDelimiter = value
		}
		
		
		
		/**
		 *   TODO Setter description ...
		 * 
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function set embededHeader( value : Boolean ) : void
		{
			EmbededHeader = value
		}
		
		
		
		/**
		 *   TODO Setter description ...
		 * 
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function set headerOverwrite( value : Boolean ) : void
		{
			HeaderOverwrite = value
		}
		
		
		
		/**
		 *   TODO Setter description ...
		 * 
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function set header( value : Array ) : void
		{
			if ( (!embededHeader && !headerHasValues) ||
				 (!embededHeader && headerHasValues && headerOverwrite) || headerOverwrite )
				   Header = value
		}
		
		
		
		// -> Public methods
		
		
		
		/**
		 *   TODO Public method description ...
		 * 
		 *   @param index int
		 *   @return Array
		 *   
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function getRecordSet( index : int ) : Array
		{
			if ( dataHasValues )
				 return data[ index ]
			else
				return null
		}
		
		
		
		/**
		 *   TODO Public method description ...
		 * 
		 *   @param recordset Array
		 *   @param index *
		 *   @return no
		 *   
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function addRecordSet( recordset : Array, index : * = null ) : void
		{
			if ( !dataHasValues )
				  data = new Array()
			
			if ( !index && index != 0 )
				  data.push( recordset )
			else
				  data.splice( index, 0, recordset )
		}
		
		
		
		/**
		 *   TODO Public method description ...
		 * 
		 *   @param startIndex int
		 *   @param endIndex int
		 *   @return Boolean
		 *   
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function deleteRecordSet ( startIndex : int, endIndex : int = 1 ) : Boolean
		{
			if ( dataHasValues && startIndex < data.length && endIndex > 0 )
				 return data.splice( startIndex, endIndex )
			else
				 return false
		}
		
		
		
		/**
		 *   TODO Public method description ...
		 * 
		 *   @param needle String or Array
		 *   @return Array
		 *   
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function search ( needle : *, removeDuplicates : Boolean = true ) : Array
		{
			var result : Array = new Array()
			for each ( var i : Array in data ){
				if ( needle is Array ){
					 for each ( var j : String in needle )
						 if ( i.indexOf( String( j ) ) >= 0 )
							  result.push( i )}
				else
					if ( i.indexOf( String( needle ) ) >= 0 ){
					 	 result.push( i )}}
			if ( removeDuplicates && result.length > 2 )
				 var k : int = result.length -1;
				 while ( k-- ){
					 var l : int = result.length;
					 while ( --l > k )
						if ( result[ k ] == result[ l ] )
							 result.splice( l, 1 )}
			return result
		}
		
		 
		/**
		 * 根据列名 和 值 获得行数
		 * */
		public function getRecordRowIndex(columnName:String,val:String):int{
			var headerindex:int=-1;
			var row:int=-1;
			for(var i:int=0;i<header.length;i++){
				if(header[i].toString()==columnName){
					headerindex = i
					break;
				}
			}
			
		 
			if(headerindex!=-1){
				for ( var j : int = 0; j < data.length; j++ )
				{
				    if(this.getRecordSet(j)[headerindex].toString()== val ){
					   row = j;
					   break;
					} 	
				}			
			}
		 return row;
		}
		
		/**
		 * 根据列名 和 值 获得整行数据
		 * */
		public function getRecordRowData(columnName:String,val:String):Array{
			var headerindex:int=-1;
			var row:int=-1;
			for(var i:int=0;i<header.length;i++){
				if(header[i].toString()==columnName){
					headerindex = i
					break;
				}
			}
			if(row!=-1){
				for ( var j : int = 0; j < data.length; j++ )
				{
					if(getRecordSet(j)[headerindex].toString()== val ){
						row = j;
						break;
					} 	
				}			
			}
			return getRecordSet(row);
		}
		
		/**
		 * 根据列名 和 值 获得整行数据
		 * */
		public function getRecordByRowColumn(row:int,columnName:String):String{
			var d:Array= new Array();
			var headerindex:int=-1;
			for(var i:int=0;i<header.length;i++){
				if(header[i].toString()==columnName){
					headerindex = i
					break;
				}
			}
			var result:String="";
			if(headerindex != -1){
			  result =getRecordSet(row)[headerindex].toString()
			}
			return result;		
		}
		
		
		public function getCell(row:int,column:int):String{
		    return  getRecordSet(row)[column].toString();
		
		}
		
		
		
		/**
		 *   TODO Private method description ...
		 * 
		 *   @param fieldNameOrIndex *
		 *   @param sequence String
		 *   @return no
		 *   
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function sort( fieldNameOrIndex : * = 0, sequence : String = 'ASC' ) : void
		{
			SortSequence = sequence
			if ( headerHasValues && header.indexOf( fieldNameOrIndex ) >=0 )
				 SortField = header.indexOf( fieldNameOrIndex )
			else
				 SortField = fieldNameOrIndex
			if ( dataHasValues )
				 data.sort ( sort2DArray )
		}
		
		
		
		/**
		 *   TODO Public method description ...
		 * 
		 *   @param raw The sting to decode
		 *   @param event Never set this, its only for internal use
		 *   
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function decode( event : Event = null ) : void
		{
			var count  : uint = 0
			var result : Array = new Array ()		 
			data = data.toString().split( recordsetDelimiter );
			for(  var i : uint = 0; i < data.length; i++ )
			{
				if( !Boolean( count % 2 ) )
					 result.push( data[ i ] )
				else
					 result[ result.length - 1 ] += data[ i ]
				count += count_( data[ i ] , fieldEnclosureToken )
			}
			result = result.filter( isNotEmptyRecord )
			result.forEach( fieldDetection )
			if ( embededHeader && headerOverwrite )
				   result.shift()
			else if ( embededHeader && headerHasValues )
				   result.shift()
			else if ( embededHeader )
				 	  Header = result.shift()
			data = result
		}
		
		
		
		/**
		 *   TODO Public method description ...
		 *   
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		public function encode () : void
		{
			var result : String = ''
			if ( headerHasValues && header.length > 0 )
			{
				 embededHeader = true
				 data.unshift( header )
			}
			if ( dataHasValues )
				 for each ( var recordset : Array in data )
				 	 result += recordset.join( fieldSeperator ) + recordsetDelimiter
			data = result
		}
		
		
		
		// -> private methods
		
		
		
		/**
		 *   TODO Private method description ...
		 * 
		 *   @param element *
		 *   @param index int
		 *   @param arr Array
		 *   @return Boolean true if recordset has values, false if not
		 *   
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		private function fieldDetection( element : *, index : int, arr : Array ) : void
		{	
			var count  : uint  = 0;
			var result : Array = new Array ();
			var tmp    : Array = element.split( fieldSeperator );
			for( var i : uint = 0; i < tmp.length; i++ )
			{
				if( !Boolean( count % 2 ) )
					 result.push( trim( tmp[ i ] ) );
				else
					 result[ result.length - 1 ] += fieldSeperator + tmp[ i ];
				count += count_( tmp[ i ] , fieldEnclosureToken );
			}
			arr[ index ] = result
		}
		
		
		
		/**
		 *   TODO Private method description ...
		 * 
		 *   @param a Array
		 *   @param b Array
		 *   @return Number
		 *   
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		private function sort2DArray( a : Array, b : Array ) : Number
		{
			var n : int = 0
			var r : int = SortSequence == 'ASC' ? -1 : 1;
			if ( String( a[ SortField ] ) < String( b[ SortField ]) )
				n = r
			else if ( String( a[ SortField ] ) > String( b[ SortField ] ) )
				n = -r
			else
				n = 0
			return n;
		}
		
		
		
		/**
		 *   TODO Private method description ...
		 * 
		 *   @param element *
		 *   @param index int
		 *   @param arr Array
		 *   @return Boolean true if recordset has values, false if not
		 *   
		 *   @langversion ActionScript 3.0
		 *   @tiptext
		 */
		private function isNotEmptyRecord( element : *, index : int, arr : Array ) : Boolean
		{
			return Boolean( trim( element ) );
		}
		
		
		
		// -> deprecated / helper methods, not inside final release
		
		
		
		public function dump() : String
		{
			var  result : String = 'data:Array -> [\r'
			for ( var i : int = 0; i < data.length; i++ )
			{
				result += '\t[' + i + ']:Array -> [\r'
				for (var j : uint = 0; j < data[i].length; j++ ) result += '\t\t[' + j + ']:String -> ' + data[ i ][ j ] + '\r'
				result += ( '\t]\r' )
			}
			result += ']\r'
			return result
		}
		
		
		private var NEWLINE_TOKENS : Array = new Array (
			'\n',
			'\r'
		);
		
		private var WHITESPACE_TOKENS : Array = new Array (
			' ',
			'\t'
		);
		
		
		/**
		 * Counts the occurrences of needle in haystack. <br />
		 * {@code trace (StringUtils.count ('hello world!', 'o')); // 2
		 * }
		 * @param haystack :String
		 * @param needle :String
		 * @param offset :Number (optional)
		 * @param length :Number (optional)
		 * @return The number of times the needle substring occurs in the
		 * haystack string. Please note that needle is case sensitive.
		 */
		private function count_ ( haystack : String, needle : String, offset : Number = 0, length : Number = 0 ) : Number
		{
			if ( length === 0 )
				length = haystack.length
			var result : Number = 0;
			haystack = haystack.slice( offset, length );
			while ( haystack.length > 0 && haystack.indexOf( needle ) != -1 )
			{
				haystack = haystack.slice( ( haystack.indexOf( needle ) + needle.length ) );
				result++;
			}
			return result;
		}
		
		
		/**
		 * Strip whitespace and newline (or other) from the beginning and end of a string. <br />
		 * {@code trace (StringUtils.trim ('hello world! ')); // hello world!
		 * }
		 * @param str :String
		 * @param charList :Array (optional)
		 * @return A string with whitespace stripped from the beginning and end
		 * of str. Without the second parameter, trim() will strip characters that
		 * defined in WHITESPACE_TOKENS and NEWLINE_TOKENS array.
		 */
		private function trim ( str : String, charList : Array = null ) : String
		{
			var list : Array;
			if ( charList )
			{
				list = charList;
			}
			else
			{
				list = WHITESPACE_TOKENS.concat( NEWLINE_TOKENS );
			}
			str = trimLeft( str, list );
			str = trimRight( str, list );
			return str;
		}
		
		/**
		 * Does the same how trim method, but only on left-side. <br />
		 * {@code trace (StringUtils.trimLeft ('hello world!')); // hello world!
		 * }
		 * @param str :String
		 * @param charList :Array (optional)
		 * @return A string with whitespace stripped from the start of str.
		 * Without the second parameter, trimLeft() will strip haracters of
		 * WHITESPACE_TOKENS + NEWLINE_TOKENS.
		 */
		private function trimLeft ( str : String, charList : Array = null ) : String
		{
			var list:Array;
			if ( charList )
				list = charList;
			else
				list = WHITESPACE_TOKENS.concat( NEWLINE_TOKENS );
			
			while ( list.toString().indexOf ( str.substr ( 0, 1 ) ) > -1 && str.length > 0 )
			{
				str = str.substr ( 1 );
			}
			return str;
		}
		
		/**
		 * Does the same how trim method, but only on right-side. <br />
		 * {@code trace (StringUtils.trimRight ('hello world!')); // hello world!
		 * }
		 * @param str :String
		 * @param charList :Array (optional)
		 * @return A string with whitespace stripped from the end of str.
		 * Without the second parameter, trimRight() will strip haracters of
		 * WHITESPACE_TOKENS + NEWLINE_TOKENS.
		 */
		private function trimRight ( str:String, charList : Array = null ) : String
		{
			var list : Array;
			if ( charList )
				list = charList;
			else
				list = WHITESPACE_TOKENS.concat( NEWLINE_TOKENS );
			
			while ( list.toString().indexOf ( str.substr ( str.length - 1 ) ) > -1 && str.length > 0)
			{
				str = str.substr ( 0, str.length - 1 );
			}
			return str;
		}
		
		
	}
	
}

/**
 * package
{
	
	import flash.events.Event
	import flash.net.URLRequest
	import flash.display.Sprite
	import com.shortybmc.data.parser.CSV;
	
	public class DocumentClass extends Sprite
	{
		
		private var csv : CSV
		
		public function DocumentClass()
		{
			example9()
		}
		
		private function example1() : void
		{
			csv = new CSV( new URLRequest('example-1.csv') );
			csv.addEventListener( Event.COMPLETE, completeHandler );
		}
		
		private function example2() : void
		{
			csv = new CSV();
			csv.addEventListener( Event.COMPLETE, completeHandler );
			csv.load( new URLRequest('example-2.csv') )
		}
		
		private function example3() : void
		{
			csv = new CSV();
			csv.fieldSeperator = ';'
			csv.addEventListener( Event.COMPLETE, completeHandler );
			csv.load( new URLRequest('example-3.csv') )
		}
		
		private function example4() : void
		{
			csv = new CSV();
			csv.fieldEnclosureToken= '\''
			csv.addEventListener( Event.COMPLETE, completeHandler );
			csv.load( new URLRequest('example-4.csv') )
		}
		
		private function example5() : void
		{
			csv = new CSV();
			csv.recordsetDelimiter= '#'
			csv.addEventListener( Event.COMPLETE, completeHandler );
			csv.load( new URLRequest('example-5.csv') )
		}
		
		private function example6() : void
		{
			csv = new CSV();
			csv.fieldSeperator = ';'
			csv.fieldEnclosureToken= '\''
			csv.recordsetDelimiter= '#'
			csv.addEventListener( Event.COMPLETE, completeHandler );
			csv.load( new URLRequest('example-6.csv') )
		}
		
		private function example7() : void
		{
			csv = new CSV();
			csv.embededHeader = false
			csv.header = ['label 1', 'label 2', 'label 3', 'label 4']
			csv.addEventListener( Event.COMPLETE, completeHandler );
			csv.load ( new URLRequest('example-8.csv') );
		}
		
		private function example8() : void
		{
			csv = new CSV();
			csv.headerOverwrite = true
			csv.header = ['My Year', 'My Make', 'My Model']
			csv.addEventListener( Event.COMPLETE, completeHandler );
			csv.load ( new URLRequest('example-7.csv') );
		}
		
		private function example9() : void
		{
			csv = new CSV();
			csv.embededHeader = false
			csv.addEventListener( Event.COMPLETE, sortExample );
			csv.load ( new URLRequest('example-8.csv') );
		}
		
		private function sortExample ( event : Event )
		{
			csv.sort( 0);
			trace ( csv.data.join('\n') )
			
			trace ( '-----------------' )
			
			csv.sort( 0, 'DES');
			trace ( csv.data.join('\n') )
			
			trace ( '-----------------' )
			
			
			csv.header = ['year', 'name', 'surname', 'friends' ]
			csv.sort('friends' );
			trace (csv.data.join('\n'));
			
			trace ( '-----------------' )
			
			csv.sort('friends', 'DES' );
			trace (csv.data.join('\n'));
		}
		
		private function example10() : void
		{
			csv = new CSV();
			csv.embededHeader = false
			csv.header = ['label 1', 'label 2', 'label 3', 'label 4' ]
			csv.addRecordSet( ['1','b','c','k'] )
			csv.addRecordSet( ['0','b','g','d'], -1 )
			csv.addRecordSet( ['2','b','c','d'],  3 )
			csv.addRecordSet( ['3','b','j','d'] )
			
			trace ( csv.header );
			trace ( csv.data.join('\n') );
			
			trace ( '-----------------' ) ;
			
			csv.deleteRecordSet( 0 )
			trace ( csv.data.join('\n') );
			
			trace ( '-----------------' ) ;
			
			var filtered : Array = csv.search( ['1', 'k', 'k', '3'], false )
			trace( filtered.join('\n') )
			
			
		}
		
		
		private function completeHandler ( event : Event )
		{
			trace ( csv.dump() )
		}
		
	}
	
}
 * */